//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================

#include <winhack.h>
#include <elasys_server.h>
#include <elapi.h>
#include <stdio.h>
#include <stdlib.h>
#include <vkey.h>
#include <driver.h>
#include <msgqueue.h>
#include <sysconfig.h>
#include <console.h>
#include <sendToScreen.h>

#define DEFAULT_AUTOPLAY_SPEED 100 // 100 times every second
#define MAX_AUTOPLAY_SPEED 1000
#define MIN_AUTOPLAY_SPEED 1
#define DEFAULT_AUTOAUTOPLAY_STARTTIME 5 // 5s
#define DEFAULT_AUTOPLAY_MESSAGEINTERVAL 10 // 10s
#define VKEY_MAX 0x100
#define VINPUT_KEYPAD   11
#define VINPUT_TOUCH    12
#define INPUTDEVICE_MOUSE    1
#define INPUTDEVICE_KEYBOARD 2
#define RECORDFILELABEL  "elastos_record_file_label"
#define READLINE 256

const wchar_t* ezVinput = L"device:vinput0";

int UserRecordFunc(int argc, wchar_t **argv);
int UserReplayFunc(int argc, wchar_t **argv);
void RecordStopFunc(void);

typedef struct _AutoPlayMaskTouchArea {
    RECT rect;
    struct _AutoPlayMaskTouchArea *pNext;
} AutoPlayMaskTouchArea;

typedef struct _AutoPlayMaskKey {
    unsigned char key;
    struct _AutoPlayMaskKey *pNext;
} AutoPlayMaskKey;

typedef struct {
    DWORD MsgType;
    union {
        TOUCH_DATA  TouchInput;
        KPD_DATA KeyInput;
    };
} VInputMessage;

static AutoPlayMaskTouchArea * s_pAutoPlayMaskTouchArea = NULL;
static AutoPlayMaskKey * s_pAutoPlayMaskKey = NULL;

HANDLE g_hThread = NULL;

FILE    *g_pDataInputFile   = NULL;
Int64   g_nPreTime          = 0;

class CVinput : public CDeviceDriver
{
public:
    CARAPI Read(
        /* [in] */ Int64 u64Offset,
        /* [in] */ Int32 bytesToRead,
        /* [out] */ MemoryBuf * pBuffer,
        /* [out] */ IEvent * * ppCompletionEvent);

    CARAPI Write(
        /* [in] */ Int64 u64Offset,
        /* [in] */ const MemoryBuf & buffer,
        /* [out] */ Int32 * pBytesWritten,
        /* [out] */ IEvent * * ppCompletionEvent);

    CARAPI Control(
        /* [in] */ Handle32 nControlCode,
        /* [in] */ const MemoryBuf & inBuffer,
        /* [out] */ MemoryBuf * pOutBuffer,
        /* [out] */ IEvent * * ppCompletionEvent);

    virtual void Dispose();
    CVinput();

public:
    IEvent *m_Event;
};

ECode CVinput::Read(
    /* [in] */ Int64 u64Offset,
    /* [in] */ Int32 bytesToRead,
    /* [out] */ MemoryBuf * pBuffer,
    /* [out] */ IEvent * * ppCompletionEvent)
{
    return E_NOT_IMPLEMENTED;
}

ECode CVinput::Write(
    /* [in] */ Int64 u64Offset,
    /* [in] */ const MemoryBuf & buffer,
    /* [out] */ Int32 * pBytesWritten,
    /* [out] */ IEvent * * ppCompletionEvent)
{
    return E_NOT_IMPLEMENTED;
}

ECode CVinput::Control(
    /* [in] */ Handle32 nControlCode,
    /* [in] */ const MemoryBuf & inBuffer,
    /* [out] */ MemoryBuf * pOutBuffer,
    /* [out] */ IEvent * * ppCompletionEvent)
{
    return E_NOT_IMPLEMENTED;
}
void CVinput::Dispose()
{
    delete this;
}
CVinput::CVinput()
{
    _CEvent_New(FALSE, UNSIGNALED, &m_Event);
}

///////////////////////////////////////////////////////////
typedef enum {
//    PLAY_CREAT,
    OPERATION_KILL,
    AUTOPLAY_START,
    AUTOPLAY_PAUSE,
    STOPTOKILL,
    RECORD_START,
    REPLAY_START
}PLAYSTATE;

PLAYSTATE g_PlayState = OPERATION_KILL;

CVinput *g_pVinput = NULL;
int SetPlayState(PLAYSTATE state, int argc, wchar_t **argv);
EXTERN_C wchar_t *   __cdecl wcstok( wchar_t *, const wchar_t *);
BOOL ParseAutoPlayMaskTouchArea(wchar_t *pwszArea, /* out */RECT *pRect)
{
    wchar_t seps[] = L",";
    wchar_t *token;

    token = wcstok(pwszArea, seps);
    if (!token) goto Error;
    pRect->left = _wtoi((wchar_t *)token);

    token = wcstok(NULL, seps);
    if (!token) goto Error;
    pRect->top = _wtoi((wchar_t *)token);

    token = wcstok(NULL, seps);
    if (!token) goto Error;
    pRect->right = _wtoi((wchar_t *)token);

    token = wcstok(NULL, seps);
    if (!token) goto Error;
    pRect->bottom = _wtoi((wchar_t *)token);
    //CONSOLEIO
    _WDistancePrint(L"\n");
    _WDistancePrint(L"autoplay: Add AutoPlayMaskTouchArea {(%d, %d), (%d, %d)).\n",
        pRect->left, pRect->top, pRect->right, pRect->bottom);

    return TRUE;

Error:
    //CONSOLEIO
    _WDistancePrint(L"\n");
    _WDistancePrint(L"\n..........ec=Error\n");
    return FALSE;
}

void GetAutoPlayMaskTouchArea()
{
    int nIdx = 0;
    wchar_t wszIdx[4];
    wchar_t wszAreaIdx[_MAX_LINE];
    wchar_t wszAreaValue[_MAX_LINE];

    while (1) {
        wcscpy(wszAreaIdx, L"AutoPlayMaskTouchArea");
        _itow(nIdx, wszIdx, 4);
        wcscat(wszAreaIdx, wszIdx);

        ECode ec = ConfigGetItemStr(wszAreaIdx, wszAreaValue);
        if (FAILED(ec)) break;

        RECT maskTouchRect;
        if (ParseAutoPlayMaskTouchArea(wszAreaValue, &maskTouchRect)) {
            AutoPlayMaskTouchArea *pMaskTouchArea;
            pMaskTouchArea = new AutoPlayMaskTouchArea;
            if (!pMaskTouchArea) return;
            pMaskTouchArea->rect = maskTouchRect;
            pMaskTouchArea->pNext = s_pAutoPlayMaskTouchArea;
            s_pAutoPlayMaskTouchArea = pMaskTouchArea;
        }

        nIdx++;
        if (nIdx > 999) {
            //CONSOLEIO
            _WDistancePrint(L"\n");
            _WDistancePrint(L"\nautoplay: Too many touch areas...\n");
            break;
        }
    }
}

void GetAutoPlayMaskKey()
{
    int nIdx = 0;
    wchar_t wszIdx[4];
    wchar_t wszAreaIdx[_MAX_LINE];

    while (1) {
        int maskKey;

        wcscpy(wszAreaIdx, L"AutoPlayMaskKey");
        _itow(nIdx, wszIdx, 4);
        wcscat(wszAreaIdx, wszIdx);

        ECode ec = ConfigGetItemInt(wszAreaIdx, &maskKey);
        if (maskKey <= 0 || maskKey > 0xFF || FAILED(ec)) break;
        //CONSOLEIO
        _WDistancePrint(L"\n");
        _WDistancePrint(L"autoplay: Add AutoPlayMaskKey %d(0x%x).\n",
            maskKey, maskKey);

        AutoPlayMaskKey *pMaskKey;
        pMaskKey = new AutoPlayMaskKey;
        if (!pMaskKey) return;
        pMaskKey->key = (unsigned char)maskKey;
        pMaskKey->pNext = s_pAutoPlayMaskKey;
        s_pAutoPlayMaskKey = pMaskKey;

        nIdx++;
        if (nIdx > 999) {
            //CONSOLEIO
            _WDistancePrint(L"\n");
            _WDistancePrint(L"autoplay: Too many keys...\n");
            break;
        }
    }
}

void FreeVinputMemory()
{
    while (s_pAutoPlayMaskTouchArea) {
        AutoPlayMaskTouchArea *pMaskTouchArea = s_pAutoPlayMaskTouchArea;
        s_pAutoPlayMaskTouchArea = s_pAutoPlayMaskTouchArea->pNext;
        delete pMaskTouchArea;
    }
    while (s_pAutoPlayMaskKey) {
        AutoPlayMaskKey *pMaskKey = s_pAutoPlayMaskKey;
        s_pAutoPlayMaskKey = pMaskKey->pNext;
        delete pMaskKey;
    }
}

BOOL IsInMaskTouchArea(short x, short y)
{
    AutoPlayMaskTouchArea *pMaskTouchArea = s_pAutoPlayMaskTouchArea;
    while (pMaskTouchArea) {
        RECT *pMaskTouchRect = &pMaskTouchArea->rect;

        if (pMaskTouchRect->left <= x && x <= pMaskTouchRect->right
            && pMaskTouchRect->top <= y && y <= pMaskTouchRect->bottom) {
            return TRUE;
        }
        pMaskTouchArea = pMaskTouchArea->pNext;
    }
    return FALSE;
}

BOOL IsMaskKey(unsigned char code)
{
    AutoPlayMaskKey *pMaskKey = s_pAutoPlayMaskKey;
    while (pMaskKey) {
        if (pMaskKey->key == code) return TRUE;
        pMaskKey = pMaskKey->pNext;
    }
    return FALSE;
}

int TouchClick(short x, short y)
{
    InputEvent inEvent;

    inEvent.button.x  = x;
    inEvent.button.y  = y;

    // Touch down
    inEvent.button.dir = 0;
    EventNotify(EVENT_TOUCH, 0, &inEvent.button);

    // Touch up
    inEvent.button.dir = 1;
    EventNotify(EVENT_TOUCH, 0, &inEvent.button);

    return 0;
}

int KeyboardClick(unsigned char code)
{
    InputEvent inEvent;
    inEvent.key.keycode = code;

    // Key down
    inEvent.key.status = KEYDOWN;
    EventNotify(EVENT_KEYPAD, 0, &inEvent.key);

    // Key up
    inEvent.key.status = KEYUP;
    EventNotify(EVENT_KEYPAD, 0, &inEvent.key);

    return 0;
}

int AutoPlayUserInputProc(void * pArg)
{
    int ret = 1;
    int nAutoPlaySpeed, nAutoPlayStartTime, nAutoPlayMessageInterval;
    int nLCDWidth, nLCDHeight;
    SystemTime timer;
    int msWait, msCounter;
    SYSTEMTIME sysTime;

    _CSystem_GetMachineTime(&timer);
    srand((unsigned int)timer.microseconds);

    // Get LCDWidth & LCDHeight
    if (NOERROR != ConfigGetItemInt(L"LCDWidth", &nLCDWidth)
        || NOERROR != ConfigGetItemInt(L"LCDHeight", &nLCDHeight)) {
        //CONSOLEIO
        _WDistancePrint(L"\n");
        _WDistancePrint(L"autoplay: Can't get LCD parameters for vinput!\n");
        return ret;
    }

    if (NOERROR != ConfigGetItemInt(L"AutoPlaySpeed", &nAutoPlaySpeed)) {
        nAutoPlaySpeed = DEFAULT_AUTOPLAY_SPEED;
    }
    else if (nAutoPlaySpeed < MIN_AUTOPLAY_SPEED) {
        nAutoPlaySpeed = MIN_AUTOPLAY_SPEED;
    }
    else if (nAutoPlaySpeed > MAX_AUTOPLAY_SPEED) {
        nAutoPlaySpeed = MAX_AUTOPLAY_SPEED;
    }

    if (NOERROR != ConfigGetItemInt(L"AutoPlayStartTime", &nAutoPlayStartTime)) {
        nAutoPlayStartTime = DEFAULT_AUTOAUTOPLAY_STARTTIME;
    }

    if (NOERROR != ConfigGetItemInt(L"AutoPlayMessageInterval",
        &nAutoPlayMessageInterval)) {
        nAutoPlayMessageInterval = DEFAULT_AUTOPLAY_MESSAGEINTERVAL;
    }
    if (nAutoPlayMessageInterval <= 0) {
        nAutoPlayMessageInterval = DEFAULT_AUTOPLAY_MESSAGEINTERVAL;
    }
    nAutoPlayMessageInterval *= 1000; // s -> ms
    //CONSOLEIO
    _WDistancePrint(L"\n");
    _WDistancePrint(L"autoplay: Auto play test will start %d seconds later...\n",
        nAutoPlayStartTime);
    _CThread_Sleep(nAutoPlayStartTime * 1000, NULL);
    GetAutoPlayMaskTouchArea();
    GetAutoPlayMaskKey();
    GetLocalTime(&sysTime);
    //CONSOLEIO
    _WDistancePrint(L"\n");
    _WDistancePrint(L"autoplay: Auto play test starts now..."\
             L"\tCurrent Time:%02d:%02d:%02d\n",
             sysTime.wHour, sysTime.wMinute, sysTime.wSecond);

    msWait = 1000 / nAutoPlaySpeed;

    msCounter = 0;
    while (g_PlayState == AUTOPLAY_PAUSE || g_PlayState == AUTOPLAY_START) {

        if (g_PlayState == AUTOPLAY_PAUSE) {
            WaitResult wr;
            ECode ec = g_pVinput->m_Event->TryWait(20*1000/*ms*/, &wr, NULL);
            if (ec == S_TIMED_OUT) {
                SetPlayState(STOPTOKILL, 0, NULL);
                _WDistancePrint(L"Vinput Stop TimerOut!");
                break;
            }
            if (g_PlayState == AUTOPLAY_PAUSE || g_PlayState == AUTOPLAY_START) {
                continue;
            }
            else {
                break;
            }
        }

        int type = rand() % 3;
        switch (type) {
            case 0:
            case 1:{
                // Touch
                int x = rand() % nLCDWidth;
                int y= rand() % nLCDHeight;

                if (IsInMaskTouchArea(x, y)) continue;

                TouchClick(x, y);
                break;
            }
            case 2:{
                // Keyboard
                int k = rand() % VKEY_MAX;
                if (IsMaskKey(k)) continue;

                KeyboardClick(k);
                break;
            }
            default:
                break;
        }
        _CThread_Sleep(msWait, NULL);
        msCounter += msWait;
        if (msCounter >= nAutoPlayMessageInterval) {
            msCounter = 0;
            GetLocalTime(&sysTime);
            //CONSOLEIO
            _WDistancePrint(L"\n");
            _WDistancePrint(L"autoplay: Current Time:%02d:%02d:%02d\n",
                sysTime.wHour, sysTime.wMinute, sysTime.wSecond);
        }
    }
    //CONSOLEIO
    _WDistancePrint(L"\n");
    SetPlayState(STOPTOKILL, 0, NULL);
    SetPlayState(OPERATION_KILL, 0, NULL);
    _WDistancePrint(L"autoplay: Auto play test stops...\n");
    ret = 0;

    return ret;
}

int SetPlayState(PLAYSTATE state, int argc, wchar_t **argv)
{
    if (state == g_PlayState) return 0;

    switch (g_PlayState) {
        case OPERATION_KILL:
            if (state == AUTOPLAY_START) {
                g_PlayState = state;//access
                g_hThread = CreateThread(
                              NULL,
                              0,
                              (LPTHREAD_START_ROUTINE)AutoPlayUserInputProc,
                              NULL,
                              0,
                              NULL
                            );
            }
            else if (state == RECORD_START) {
                g_PlayState = state;//access
                UserRecordFunc(argc, argv);
            }
            else if (state == REPLAY_START) {
                g_PlayState = state;//access
                UserReplayFunc(argc, argv);
            }
            break;
        case STOPTOKILL:
            if (state == OPERATION_KILL)
                g_PlayState = state;
            break;
        case AUTOPLAY_START:
            if (state == AUTOPLAY_PAUSE) {
                g_PlayState = state;//access
            }
            else if (state == STOPTOKILL) {//kill
                g_PlayState = state;
                g_pVinput->m_Event->Notify(SIGNALED);
            }
            else {
                _WDistancePrint(L"Must stop the current operation first!\n");
            }
            break;
        case AUTOPLAY_PAUSE:
            if (state == AUTOPLAY_START) {
                g_PlayState = state;//access
                g_pVinput->m_Event->Notify(SIGNALED);
            }
            else if (state == STOPTOKILL) {//kill
                g_PlayState = state;
                g_pVinput->m_Event->Notify(SIGNALED);
            }
            else {
                _WDistancePrint(L"Must stop the current operation first!\n");
            }
            break;
        case RECORD_START:
            if (state == STOPTOKILL) {//kill
                RecordStopFunc();
                g_PlayState = OPERATION_KILL;
            }
            else {
                _WDistancePrint(L"Must stop the current operation first!\n");
            }
            break;
        case REPLAY_START:
            if (state == STOPTOKILL) {//kill
                g_PlayState = state;
            }
            else {
                _WDistancePrint(L"Must stop the current operation first!\n");
            }
            break;
        default:
            break;
    }
    return 0;
}

//BUGBUG:ues this function can start more than one thread
int DoAutoPlay(int argc, wchar_t **argv)
{
    SetPlayState(AUTOPLAY_START, argc, argv);
    return 0;
}

inline UINT MyGetSystemTime()
{
    SystemTime time1;
    _CSystem_GetMachineTime(&time1);

    return (time1.seconds * 1000000 + time1.microseconds);
}

void OperationRecord(int id, int nCode, void *pParam)
{

    Int64 n64Time = MyGetSystemTime();

    UINT udelay;

    udelay = (UINT)((n64Time - g_nPreTime) / 1000);

    g_nPreTime = n64Time;

    int nFlag = 0;
    switch (nCode) {
        //VINPUT_TOUCH
        case 1:
            nFlag = INPUTDEVICE_MOUSE;
            fprintf(g_pDataInputFile, "%d  %8u  ", nFlag, udelay);
            fprintf(g_pDataInputFile, "%8d  %8d  %8d\n",
                                ((TOUCH_DATA *)pParam)->dir,
                                ((TOUCH_DATA *)pParam)->x,
                                ((TOUCH_DATA *)pParam)->y);
            break;
        //VINPUT_KEYPAD
        case 0:
            nFlag = INPUTDEVICE_KEYBOARD;
            fprintf(g_pDataInputFile, "%d  %8u  ", nFlag, udelay);
            fprintf(g_pDataInputFile, "  %08x  %08x\n",
                        ((KPD_DATA *)pParam)->keycode,
                        ((KPD_DATA *)pParam)->status);
            break;
        default:
            break;
    }
}

INT OperationRead(void * pArg)
{
    InputEvent inEvent;
    UINT Delay;
    int nFlag = 0;
    char szBuf[READLINE];
    while (!feof(g_pDataInputFile) && REPLAY_START == g_PlayState) {
        memset(szBuf, '\0', READLINE);
        if (NULL == fgets(szBuf, READLINE, g_pDataInputFile)) {
            break;
        }

        if (szBuf[0] == '#') continue;
        sscanf(szBuf, "%d", &nFlag);

        switch (nFlag) {
            case INPUTDEVICE_MOUSE:
                // Touch
                sscanf(szBuf, "%d %u %d %d %d", &nFlag, &Delay,
                    &inEvent.button.dir, &inEvent.button.x, &inEvent.button.y);
                _CThread_Sleep(Delay, NULL);
                EventNotify(EVENT_TOUCH, 0, &inEvent.button);
                break;
            case INPUTDEVICE_KEYBOARD:
                // Keyboard
                sscanf(szBuf, "%d %u %x %x", &nFlag, &Delay,
                    &inEvent.key.keycode, &inEvent.key.status);
                _CThread_Sleep(Delay, NULL);

                EventNotify(EVENT_KEYPAD, 0, &inEvent.key);
                break;
            default:
                break;
        }
    }

    if (NULL != g_pDataInputFile) {
        fclose(g_pDataInputFile);
        g_pDataInputFile = NULL;
        _WDistancePrint(L"Opertion replay stopped. \n");
    }
    SetPlayState(STOPTOKILL, 0, NULL);
    SetPlayState(OPERATION_KILL, 0, NULL);
    return 0;
}

int DoRecord(int argc, wchar_t **argv)
{
    SetPlayState(RECORD_START, argc, argv);
    return 0;
}

int UserRecordFunc(int argc, wchar_t **argv)
{
    wchar_t pwszFileName[_MAX_LINE];
    if (NULL == argv[1]) {
        wcscpy(pwszFileName, L"vinuptdefault.dat");
    }
    else {
        wcscpy(pwszFileName, argv[1]);
        if (NULL == wcsstr(pwszFileName, L".dat")) {
            wcscat(pwszFileName, L".dat");
        }
    }

    if (NULL != g_pDataInputFile) return 1;
    g_pDataInputFile = _wfopen(pwszFileName, L"w+");

    if (NULL == g_pDataInputFile) {
        _WDistancePrint(L"Error: Can not open %s, record stop!\n", pwszFileName);
        return 1;
    }
    fprintf(g_pDataInputFile, "%s\n", RECORDFILELABEL);
    g_nPreTime = MyGetSystemTime();
    _SysSetHook(HOOK_INPUTEVENT, (void *)OperationRecord);
    _WDistancePrint(L"Record Opertion and save in file %s.\n", pwszFileName);
    return 0;
}

void RecordStopFunc(void)
{
    _SysSetHook(HOOK_INPUTEVENT, NULL);
    g_nPreTime = 0;
    if (NULL != g_pDataInputFile) {
        fclose(g_pDataInputFile);
        g_pDataInputFile = NULL;
    }
    _WDistancePrint(L"Opertion record stopped.\n");
    return;
}

int DoReplay(int argc, wchar_t **argv)
{
    SetPlayState(REPLAY_START, argc, argv);
    return 0;
}

int UserReplayFunc(int argc, wchar_t **argv)
{
    wchar_t pwszFileName[_MAX_LINE];
    char    pszlabelName[_MAX_LINE];

    if (NULL == argv[1]) {
        wcscpy(pwszFileName, L"vinuptdefault.dat");
    }
    else {
        wcscpy(pwszFileName, argv[1]);
        if (NULL == wcsstr(pwszFileName, L".dat")) {
            wcscat(pwszFileName, L".dat");
        }
    }

    if (NULL != g_pDataInputFile) return 1;
    g_pDataInputFile = _wfopen(pwszFileName, L"r");
    if (NULL == g_pDataInputFile) {
        _WDistancePrint(L"Error: Can not open %s, record stop!\n", pwszFileName);
        return 2;
    }
    fseek(g_pDataInputFile, 0, SEEK_SET);
    fscanf (g_pDataInputFile, "%s\n",pszlabelName);
    if (0 != strcmp(pszlabelName, RECORDFILELABEL)) {
        _WDistancePrint(L"Error: The file %s is not record file!\n", pwszFileName);
        return 3;
    }

    _CThread_New((PThreadMain)OperationRead, NULL, 0, NULL);
    _WDistancePrint(L"Opertion replay started in file %s.\n", pwszFileName);
    return 0;
}

int DoStop(int argc, wchar_t **argv)
{

    SetPlayState(STOPTOKILL, 0, NULL);
    return 0;
}

ConfigConsoleCmd g_vinputCommands[] =  {
    {                                                /* CMDS_LOAD   */
        L"autoplay", DoAutoPlay,
        L"autoplay   - auto input or play operation record",
        L"autoplay:  auto input or play recorded operation.\nFormat:     autoplay"
    },
    {                                                /* CMDS_LOAD   */
        L"stop", DoStop,
        L"stop       - stop the current operation",
        L"stop:      stop the current operation.\nFormat:      stop"
    },
    {                                                /* CMDS_LOAD   */
        L"replay", DoReplay,
        L"replay     - play the operation record",
        L"replay:    play the operation record.\nFormat:      replay [record_file_name]"
    },
    {                                                /* CMDS_LOAD   */
        L"record", DoRecord,
        L"record     - record the operation",
        L"record:    record the operation.\nFormat:      record [record_file_name]"
    },
    {
        NULL, NULL, NULL, NULL
    }
};

ECode RegisterCommand(void)
{
    ECode ec = NOERROR;
    for (int i = 0; NULL != g_vinputCommands[i].pwszCmdName; i++) {
        ec = _SysRegisterCommand(&g_vinputCommands[i]);
        if (NOERROR != ec)
            return ec;
    }
    return NOERROR;
}

ECode UnregisterCommand(void)
{
    ECode ec = NOERROR;
    for (int i = 0; NULL != g_vinputCommands[i].pwszCmdName; i++) {
        ec = _SysUnregsterRegisterCommand(g_vinputCommands[i].pwszCmdName);
        if (NOERROR != ec)
            return ec;
    }
    return ec;
}

void OnChangePlayState(int eType, int nCode, void *pParam)
{
    assert(eType == EVENT_PAUSEBREAK);
    if (g_PlayState == AUTOPLAY_START) {
        SetPlayState(AUTOPLAY_PAUSE, 0, NULL);
    }
    else {
        SetPlayState(AUTOPLAY_START, 0, NULL);
    }
}

int g_nVInputEnable = 0;

EXTERN_C void InitVInput()
{
    g_pVinput = new CVinput();
    if (!g_pVinput) return;

    if (NOERROR != ConfigGetItemInt(L"AutoPlayEnable", &g_nVInputEnable)) {
        g_nVInputEnable = 0;
    }

    if (g_nVInputEnable) {
        ECode ec = _CSystem_RegisterRunningObject(ezVinput, (PInterface)g_pVinput);
        if (FAILED(ec)) {
            g_nVInputEnable = 0;
            delete g_pVinput;
            //CONSOLEIO
            _WDistancePrint(L"\n");
            _WDistancePrint(L"Load vinput driver failed!\n");
            return;
        }
        RegisterCommand();
        g_pVinput->AddRef();

        //Addtion: Hook Key<PauseBreak> Press event
        AddNotify(EVENT_PAUSEBREAK, (void *)OnChangePlayState);
    }
}

EXTERN_C void ReleaseVInput()
{
//    DWORD dwECodeScreen = 0;
    if (g_nVInputEnable) {
        SetPlayState(STOPTOKILL, 0, NULL);
//        if (g_PlayState == AUTOPLAY_PAUSE || g_PlayState == AUTOPLAY_START) {
//            GetExitCodeThread(g_hThread, &dwECodeScreen);
//            TerminateThread(g_hThread, dwECodeScreen);
//        }
        if (NULL != g_pDataInputFile) {
            fclose(g_pDataInputFile);
            g_pDataInputFile = NULL;
        }
        UnregisterCommand();
        FreeVinputMemory();
        _CSystem_UnregisterRunningObject(ezVinput);
        delete g_pVinput;
    }
}
